﻿using System.Collections.Generic;
using System;
using System.Runtime.Serialization;

namespace BrainTechLLC
{    
    [Serializable]
    [DataContract]
    public class MatchAndStrength
    {
        [DataMember]
        public object MatchedItem { get; set; }
        [DataMember]
        public string MatchString { get; set; }
        [DataMember]
        public float MatchMeasure { get; set; }
    }

    public static class SearchExt
    {
        public static float FindByKeyword<T>(this IEnumerable<T> things, string matchString, out string matched, ref List<MatchAndStrength> matchedItems, float min)
            where T : class, IAssociatedID
        {
            matched = string.Empty;
            float fMax = 0.0f;

            if (matchedItems == null)
                matchedItems = new List<MatchAndStrength>();

            foreach (IGameThing thing in things)
            {
                string bestMatch;
                float f = thing.KeywordMatchStrength(matchString, out bestMatch);
                if (f >= min)
                {
                    if (f > fMax)
                    {
                        fMax = f;
                        matched = bestMatch;
                    }
                    MatchAndStrength m = new MatchAndStrength()
                    {
                        MatchedItem = thing,
                        MatchString = bestMatch,
                        MatchMeasure = f
                    };
                    matchedItems.Add(m);
                }
            }
            matchedItems.Sort((a, b) => b.MatchMeasure.CompareTo(a.MatchMeasure));

            return fMax;
        }

        public static List<T> FindUsingString<T>(this AssociatedIDCollection assocIDsInRoom, string input, float threshold, FindOptions options, params Type[] types) where T : class, IGameThing
        {
            if (threshold == 0f)
            {
                threshold = 0.55f;
            }

            string[] nextWords = input.Split(" ".ToCharArray(), 2, StringSplitOptions.None);

            if (nextWords.Length < 1)
                return null;

            List<MatchAndStrength> matchedItems = new List<MatchAndStrength>();
            string matched;

            var results = new List<T>();

            if ((options & FindOptions.SameRoom) == FindOptions.SameRoom)
            {
                if (assocIDsInRoom != null)
                {
                    if (assocIDsInRoom.FindByKeyword(nextWords[0], threshold, out matched, ref matchedItems) > threshold)
                    {
                        for (int n = 0; n < matchedItems.Count; n++)
                        {
                            IGameThing thing = matchedItems[n].MatchedItem as IGameThing;

                            Type t = thing.GetType();

                            if (!typeof(T).IsAssignableFrom(t))
                                continue;

                            results.Add(thing as T);

                            if ((options & FindOptions.WantSingle) == FindOptions.WantSingle)
                                break;
                        }
                    }
                }
            }
            
            if ((options & FindOptions.InWorld) == FindOptions.InWorld)
            {
                if ((options & FindOptions.WantSingle) != FindOptions.WantSingle || results.Count == 0)
                {
                    List<T> all;

                    if (types == null || types.Length == 0)
                        all = Everything.FindAllThingsOfType<T>();
                    else
                        all = Everything.FindAllThingsOfType<T>(types);

                    if (all.FindByKeyword<T>(nextWords[0], out matched, ref matchedItems, threshold) > threshold)
                    {
                        for (int n = 0; n < matchedItems.Count; n++)
                        {
                            T thing = matchedItems[n].MatchedItem as T;

                            results.Add(thing as T);

                            if ((options & FindOptions.WantSingle) == FindOptions.WantSingle)
                                break;
                        }
                    }
                }
            }

            return results;
        }

        public static float Find<T>(this string keyword, ref List<MatchAndStrength> matchedItems, float threshold, params Type[] types) where T : class, IAssociatedID
        {
            if (threshold == 0f)
                threshold = 0.55f;

            List<T> start;

            if (types == null || types.Length == 0)
                start = Everything.FindAllThingsOfType<T>();
            else
                start = Everything.FindAllThingsOfType<T>(types);

            string bestMatch;
            return start.FindByKeyword(keyword, out bestMatch, ref matchedItems, threshold);
        }

        public static float FindByKeyword(this IAssociatedIDCollection coll, string matchString, float threshold, out string matched, ref List<MatchAndStrength> matchedItems)
        {
            if (threshold == 0f)
                threshold = 0.55f;

            List<IAssociatedID> things = coll.ReadItems<IAssociatedID>();
            return things.FindByKeyword(matchString, out matched, ref matchedItems, threshold);
        }
    }

}
